# @Time : 2024-10-28 16:10
# @Author : Fioman 
# @Phone : 13149920693
"""
1) 可迭代对象(Iterable)
任何可以直接用于循环遍历的对象.实现了__iter__()方法或者是__getitem__()方法. for循环的本质上是调用
iter()方法,它会尝试调用__iter__,如果不存在,会改为检查__getitem__(),并且从索引0开始调用,直到抛出
IndexError为止. 虽然__getitem__是可以迭代的,但是并未实现Python的迭代协议.

2) Python的collections.abc中的Iterable对象是指实现了__iter__这个魔法函数的对象.
可迭代协议也指的是__iter__()方法,只要实现了__iter__()的类对象,都可以被认为是Iterable对象

3) 迭代器(Iterator)
在可迭代对象的基础上,又实现了__next__()的方法,称为迭代器

4) 内置函数iter(iterable) 可以获取迭代器对象Iterator,然后通过next(Iterator),可以获取访问下一个元素
"""

# region 1. 可迭代对象举例 __iter__() or __getitem__()
from collections.abc import Iterable


class MyIterable():
    # def __iter__(self):
    #     return self
    def __getitem__(self, index):
        return "abc"[index]


obj1 = MyIterable()
print(isinstance(obj1, Iterable))  # False ,但是可以遍历
for elem in obj1:
    print(elem)

# endregion

# region 2. 可迭代对象的本质
"""
1) 当要迭代一个对象的时候,系统会调用iter(obj)方法.
2) 内置的iter(obj)会检查对象是否实现了__iter__()方法,如果实现了__iter__()方法,调用它获取一个迭代器
3) 如果没有实现__iter__()方法,但是实现了__getitem__()方法,会创建一个迭代器,按照顺序(从索引0开始)获取
    元素,知道报IndexError
4) 如果上面两种都没有,就会抛出异常
"""


class Simple:
    def __iter__(self):
        return [1, 2, 3].__iter__()


s = Simple()  # True
print(isinstance(s, Iterable))
for elem in s:
    print(elem)


class Simple2:
    def __iter__(self):
        pass

    def __getitem__(self, index):
        return "abc"[index]


s = Simple2()
print(isinstance(s, Iterable))  # True
# TypeError: iter() returned non-iterator of type 'NoneType'
# 即使实现了__getitem__也无用,只要实现了__iter__,就优先调用__iter__

try:
    for elem in s:  # 迭代的时候胡报错,因为__iter__没有返回一个迭代器
        print(elem)
except Exception as e:
    print(str(e))

# endregion

# region 3. 迭代器
"""
1) 实现__iter__()方法,一般返回自身
2) __next__() 方法, 实现迭代逻辑.
虽然__next__()方法是实现迭代器的关键,但是__iter__()方法是迭代器协议的重要部分.
它提供了统一的迭代接口,iter()方法被调用的时候,就会走__iter__()方法返回一个迭代器,一般是返回自身.
iter_obj = iter(my_obj)
1) 调用 __iter__() 方法。
2) 检查返回的对象是否具有 __next__() 方法。
所以迭代器必须是实现了__iter__()方法和__next__()方法的对象
可迭代对象,是实现了__iter__()方法 ,或者是实现了__getitem__()方法的对象.
"""

"""
虽然实现了__iter__方法,表示这个对象是Iterable类型的,但是它不一定就可以迭代成功,要迭代成功的前提
还必须是__iter__()方法返回一个迭代器,它还必须实现__next__()方法.
"""


class OnlyIter:
    def __iter__(self):
        return self


try:
    obj = OnlyIter()
    print(isinstance(obj, Iterable))
    for elem in obj:
        print(elem)
except Exception as e:
    print(str(e))
# endregion

"""
迭代器实现:
1) __iter__() 返回一个带有__next__()的对象,调用iter(obj)的时候会被隐式调用.
2) __next__() 逐一的访问容器中的元素.当访问到最后一个元素的下一个元素的时候,引发StopIteration异常
    调用next()的时候,会隐式的调用__next__() 
3) 迭代器可以通过内置的函数iter(iterable)获取
"""


# region 3. 自定义迭代器,模拟Range()函数
class MyRange(object):
    def __init__(self, start, end, offset=1):
        self.start = start
        self.end = end
        self.offset = offset
        self.value = start

    def __iter__(self):
        return self

    def __next__(self):
        cur = self.value
        self.value += self.offset
        if self.value >= self.end:
            raise StopIteration
        return cur


for i in MyRange(0, 10, 2):
    print(i, end=" ")
    
# endregion
